package com.whub507.msgmanager.sso.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.whub507.msgmanager.sso.client.auth.AuthUtils;
import com.whub507.msgmanager.sso.client.auth.IdentityToken;
import com.whub507.msgmanager.sso.client.sso.SimpleClientHttpRequestFactory4Https;
import com.whub507.msgmanager.sso.context.ContextUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.Map;

@Component
public class SessionInterceptor implements HandlerInterceptor {

	/**
	 * 统一认证地址
	 */
	@Value("${drap_platform.sso_server.rootpath_proxy}/aa/static/main/login/login.html")
	private String login_url_sso;

	@Value("${drap_platform.sso_server.rootpath}/auth/refreshToken")
	private String refreshToken_url;

	@Value("${drap_platform.sso_server.rootpath}/auth/getAppPublicKey")
	private String getAppPublicKey_url;

	/**
	 * 应用登录入口
	 */
	@Value("${drap_platform.sso_server.login_target:http://100.81.20.208:9090/login}")
	private String login_url_app;

	/**
	 * 应用唯一标识,对应统一权限分系统中应用ID
	 */
	private String app_id = "sjdx";

	/**
	 * 刷新区间
	 */
	private final int exp_distance = 30 * 60 * 1000;

	private RestTemplate restTemplate;

	@Autowired
	private ObjectMapper mapper;

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 当本应用对外提供接口，可通过此方法确认调用方为统一权限管理的应用
		// 优先检查应用调用接口的场景
		if (checkAppCosumerAPI(request)) {
			return true;
		}

		ContextUser user = (ContextUser) request.getSession().getAttribute("user");
//		return true;
		// 存在会话，继续执行
		if (user != null) {
//			// token将用于应用调用微服务（经过边缘服务）的有效凭证，需要保证有效期，适时刷新
//			autoRefreshToken(user);
			return true;
		}
		System.out.println("NO USER");
		if (isAjaxRequest(request)) {// ajax请求直接返回403
			response.sendError(HttpStatus.FORBIDDEN.value(), "Session is invalid.");
		} else {
				// 引导用户去统一认证,并指定认证成功重定向回应用登录入口,SSO Server检查用户是否具备访问app_id的权限，并根据app_id记录用户访问的应用集
				// sso_way=sso_code 标记ssoserver 以一次性认证码方式重定向给redirect_url
				// 保证redirect_url完整性，需要encode
				// SSO Server会检查redirect_url的合法性，注册应用时请填写正确应用访问地址（与redirect_url的host一致）
//			response.sendRedirect(login_url_sso + "?sso_page=true&"+"app_id=" + app_id + "&sso_way=sso_code&redirect_url="
//					+ URLEncoder.encode(login_url_app, "UTF-8"));
			response.setHeader( "Access-Control-Allow-Origin","*" );
			response.sendRedirect("http://100.81.20.208:9090/welcome");
		}

		return false;
	}

	public static boolean isAjaxRequest(HttpServletRequest request) {
		String requestedWith = request.getHeader("x-requested-with");
		if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 不再使用sso_token调用统一权限分系统接口，而是使用app_sign机制 过期前预定时间内,更新sso_token,防止活动状态下jwt过期
	 * 
	 * @param user
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	private void autoRefreshToken(ContextUser user) throws Exception {
//		String payload = new String(Base64.decodeBase64(user.getToken().split("\\.")[1]),"UTF-8");
//		IdentityToken idToken = mapper.readValue(payload.getBytes("UTF-8"), IdentityToken.class);
		
		Jwt dt = JwtHelper.decode(user.getToken());
		IdentityToken idToken = mapper.readValue(dt.getClaims(), IdentityToken.class);
		
		long range = idToken.getExp() - System.currentTimeMillis();

		if (range < exp_distance) {
			HttpHeaders requestHeaders = new HttpHeaders();
			requestHeaders.add("ssotoken", user.getToken());
			// 告知SSO Server用户访问的应用
			requestHeaders.add("appid", app_id);
			HttpEntity<String> requestEntity = new HttpEntity<String>(null, requestHeaders);
			ResponseEntity<Map> response = restTemplate.exchange(refreshToken_url, HttpMethod.GET, requestEntity,
					Map.class);
			Map result = response.getBody();
			String newToken = (String) ((Map) result.get("data")).get("sso_token");
			user.setToken(newToken);
		}

	}

	@PostConstruct
	public void init() {
		SimpleClientHttpRequestFactory4Https requestFactory = new SimpleClientHttpRequestFactory4Https();

		requestFactory.setReadTimeout(120 * 1000);

		this.restTemplate = new RestTemplate(requestFactory);
	}

	/**
	 * 检查应用按规范安全访问接口 appsign = 加密(app_id+time_stamp)
	 * 
	 * @param request
	 * @return
	 */
	private boolean checkAppCosumerAPI(HttpServletRequest request) {
		try {
			String app_id = request.getHeader("appid");
			String time_stamp = request.getHeader("timestamp");
			String app_sign = request.getHeader("appsign");
			if (StringUtils.isEmpty(app_id) || StringUtils.isEmpty(time_stamp) || StringUtils.isEmpty(app_sign)) {
				return false;
			}
			long time = Long.parseLong(time_stamp);
			// 签名有效期幅度小于5分钟
			if (Math.abs(System.currentTimeMillis() - time) > 5 * 60 * 1000) {
				throw new RuntimeException("The time_stamp with delay beyond " + 5 * 60 + " seconds.");
			}

			// TODO 可考虑缓存应用公钥
			String public_key = restTemplate.getForObject(getAppPublicKey_url + "?app_id=" + app_id, String.class);

			if (StringUtils.isEmpty(public_key)) {
				throw new RuntimeException("The app[" + app_id + "] has no public_key.");
			}

			String text = AuthUtils.decrypt(public_key, app_sign);

			return text.equals(app_id + time_stamp);

		} catch (Exception e) {
			logger.warn("SessionInterceptor checkAppCosumerAPI failed,because " + e.getMessage());
			return false;
		}
	}

}
